﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;

namespace PluginAnalyzer
{
    public abstract class AttributeCheckPolicy
    {
        public SemanticModel SemanticModel { get; set; }

        string _fullName;
        public AttributeCheckPolicy(string fullName)
        {
            _fullName = fullName;
        }

        public string FullName
        {
            get
            {
                return _fullName;
            }
        }

        public static string Prefix(IPropertySymbol property)
        {
            return "属性：" + property.ContainingType.Name + "." + property.Name + "不正确，";
        }
        public static bool IsGenericList(ITypeSymbol type)
        {
            return IsGenericType(type) && type.Name == "List";
        }

        public string RequireType(AttributeData attribute, IPropertySymbol property, params string[] types)
        {
            if (types.All(t => property.Type.ToString() != t))
            {
                return Prefix(property) + "标注了 " + attribute.AttributeClass.Name + " 属性, 类型应该为 " + string.Join(" 或者 ", types.ToArray());
            }
            return null;
        }
        public static bool IsGenericType(ITypeSymbol typeSymbol)
        {
            return typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType;
        }
        public static ITypeSymbol GetGenericType(ITypeSymbol typeSymbol)
        {
            if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType)
            {
                return namedTypeSymbol.ConstructedFrom;
            }

            return null;
        }

        public static IReadOnlyList<ITypeSymbol> GetGenericTypeArguments(ITypeSymbol typeSymbol)
        {
            if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType)
            {
                return namedTypeSymbol.TypeArguments;
            }

            return null;
        }

        public abstract string Check(AttributeData attribute, IPropertySymbol property);

        public ExpressionSyntax GetAttributePropertyValue(AttributeData attribute, string propertyName)
        {
            try
            {
                var arguments = (attribute.ApplicationSyntaxReference.GetSyntax() as AttributeSyntax)?.ArgumentList?.Arguments;
                if (arguments != null)
                {
                    var arg = arguments.Value.FirstOrDefault(i => i.NameEquals?.Name?.ToString() == propertyName);
                    if (arg.Expression is TypeOfExpressionSyntax typeOfExpression)
                    {
                        return typeOfExpression;
                    }
                    else
                    {
                        return arg.Expression;
                    }
                }
                return null;
            }
            catch
            {
                return null;
            }
        }

        public static bool IsType(ITypeSymbol derivedTypeSymbol, string baseTypeFullName)
        {
            if (derivedTypeSymbol == null)
            {
                return false;
            }

            if (derivedTypeSymbol.ToDisplayString() == baseTypeFullName)
            {
                return true;
            }

            if (Enumerable.Any(derivedTypeSymbol.Interfaces, @interface => @interface.ToDisplayString() == baseTypeFullName))
            {
                return true;
            }

            return IsType(derivedTypeSymbol.BaseType, baseTypeFullName);
        }
    }

    public class TextPropertyAttributeChecker : AttributeCheckPolicy
    {
        public TextPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.TextPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string");
        }
    }

    public class FormulaPropertyAttributeChecker : AttributeCheckPolicy
    {
        public FormulaPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.FormulaPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "object");
        }
    }

    public class IntPropertyAttributeChecker : AttributeCheckPolicy
    {
        public IntPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.IntPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            var allowNull = GetAttributePropertyValue(attribute, "AllowNull");

            if (allowNull?.ToString()?.ToLower() == "true")
            {
                return RequireType(attribute, property, "int?");
            }
            else
            {
                return RequireType(attribute, property, "int");
            }
        }
    }

    public class DoublePropertyAttributeChecker : AttributeCheckPolicy
    {
        public DoublePropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.DoublePropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            var allowNull = GetAttributePropertyValue(attribute, "AllowNull");

            if (allowNull?.ToString()?.ToLower() == "true")
            {
                return RequireType(attribute, property, "double?");
            }
            else
            {
                return RequireType(attribute, property, "double");
            }
        }
    }

    public class PercentagePropertyAttributeChecker : AttributeCheckPolicy
    {
        public PercentagePropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.PercentagePropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "double");
        }
    }

    public class BoolPropertyAttributeChecker : AttributeCheckPolicy
    {
        public BoolPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.BoolPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "bool");
        }
    }

    public class ComboPropertyAttributeChecker : AttributeCheckPolicy
    {
        public ComboPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.ComboPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string", "int", "bool", "object");
        }
    }
    public class RadioGroupPropertyAttributeChecker : AttributeCheckPolicy
    {
        public RadioGroupPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.RadioGroupPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string", "int", "bool", "object");
        }
    }

    public class ColorPropertyAttributeChecker : AttributeCheckPolicy
    {
        public ColorPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.ColorPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string", "object");
        }
    }
    public class ImageValuePropertyAttributeChecker : AttributeCheckPolicy
    {
        public ImageValuePropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.ImageValuePropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "object", "GrapeCity.Forguncy.Plugin.ImageValue");
        }
    }

    public class CustomCommandObjectAttributeChecker : AttributeCheckPolicy
    {
        public CustomCommandObjectAttributeChecker()
            : base("GrapeCity.Forguncy.Commands.CustomCommandObjectAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "object", "GrapeCity.Forguncy.Commands.CustomCommandObject");
        }
    }

    public class BindingDataSourcePropertyAttributeChecker : AttributeCheckPolicy
    {
        public BindingDataSourcePropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.BindingDataSourcePropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "object", "GrapeCity.Forguncy.Plugin.IBindingDataSource");
        }
    }
    public class ObjectPropertyAttributeChecker : AttributeCheckPolicy
    {
        public ObjectPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.ObjectPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            List<string> errors = new List<string>();
            var objType = GetAttributePropertyValue(attribute, "ObjType");
            if (objType == null)
            {
                errors.Add(Prefix(property) + "标注了 ObjectPropertyAttribute 属性的类型必须设置 ObjType ");
            }
            else
            {
                if (objType is TypeOfExpressionSyntax expressionSyntax)
                {
                    var typeinfo = SemanticModel.GetTypeInfo(expressionSyntax.Type);
                    if (!IsType(typeinfo.Type, property.Type.ToString()))
                    {
                        errors.Add(Prefix(property) + "标注了 ObjectPropertyAttribute 属性 ObjType 必须是属性类型的子类");
                    }
                }
                if (!IsType(property.Type, "System.ICloneable"))
                {
                    errors.Add(Prefix(property) + "标注了 ObjectPropertyAttribute 属性的类型必须是一个从 ObjectPropertyBase 派生或实现ICloneable接口的类");
                }
            }
            return string.Join("\r\n", errors);
        }
    }

    public class ListPropertyAttributeChecker : AttributeCheckPolicy
    {
        public ListPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.ListPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            if (!IsGenericList(property.Type))
            {
                return Prefix(property) + "标注了 ListPropertyAttribute 属性的类型是泛型的List";
            }
            return null;
        }
    }

    public class ObjectListPropertyAttributeChecker : AttributeCheckPolicy
    {
        public ObjectListPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.ObjectListPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            if (!IsGenericList(property.Type))
            {
                return Prefix(property) + "标注了 ObjectListPropertyAttribute 属性的类型是泛型的List";
            }
            if (!IsType(GetGenericTypeArguments(property.Type)[0], "GrapeCity.Forguncy.Plugin.INamedObject"))
            {
                return Prefix(property) + "标注了 ObjectListPropertyAttribute 属性的泛型必须是 INamedObject ";
            }
            var objType = GetAttributePropertyValue(attribute, "ItemType");
            if (objType == null)
            {
                return Prefix(property) + "标注了 ObjectListPropertyAttribute， 必须指定 ItemType 属性";
            }
            if (objType is TypeOfExpressionSyntax typeOfExpression)
            {
                var typeinfo = SemanticModel.GetTypeInfo(typeOfExpression.Type);
                if (typeinfo.Type != null)
                {
                    if (!IsType(typeinfo.Type, GetGenericTypeArguments(property.Type)[0].ToString()))
                    {
                        return Prefix(property) + "标注了 ObjectPropertyAttribute 属性 ItemType 必须是属性类型的子类";
                    }

                    if (!IsType(typeinfo.Type, "System.ICloneable"))
                    {
                        return Prefix(property) + "标注了 ObjectListPropertyAttribute， ItemType 属性对应的类型应该从 ObjectPropertyBase 派生或实现ICloneable接口";
                    }
                    if (!IsType(typeinfo.Type, "GrapeCity.Forguncy.Plugin.INamedObject"))
                    {
                        return Prefix(property) + "标注了 ObjectListPropertyAttribute， ItemType 属性对应的类型应该实现 INamedObject 接口";
                    }
                }
            }
            return null;
        }
    }

    public class TreePropertyAttributeChecker : AttributeCheckPolicy
    {
        public TreePropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.TreePropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            if (!IsGenericList(property.Type) || !IsType(GetGenericTypeArguments(property.Type)[0], "GrapeCity.Forguncy.Plugin.ITreeNode"))
            {
                return Prefix(property) + "标注了 TreePropertyAttribute 属性的类型必须是 List<ITreeNode>";
            }

            var objType = GetAttributePropertyValue(attribute, "NodeType");
            if (objType == null)
            {
                return Prefix(property) + "标注了 TreePropertyAttribute， 必须指定 NodeType 属性";
            }
            if (objType is TypeOfExpressionSyntax typeOfExpression)
            {
                var typeinfo = SemanticModel.GetTypeInfo(typeOfExpression.Type);
                if (typeinfo.Type != null)
                {
                    if (!IsType(typeinfo.Type, "System.ICloneable"))
                    {
                        return Prefix(property) + "标注了 TreePropertyAttribute， NodeType 属性对应的类型应该从 ObjectPropertyBase 派生或实现 ICloneable 接口";
                    }
                    if (!IsType(typeinfo.Type, "GrapeCity.Forguncy.Plugin.ITreeNode"))
                    {
                        return Prefix(property) + "标注了 TreePropertyAttribute， NodeType 属性对应的类型应该实现 ITreeNode 接口";
                    }
                }
            }

            return null;
        }
    }

    public class FontFamilyPropertyAttributeChecker : AttributeCheckPolicy
    {
        public FontFamilyPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.FontFamilyPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string");
        }
    }

    public class RoleSelectorPropertyAttributeChecker : AttributeCheckPolicy
    {
        public RoleSelectorPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.RoleSelectorPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "System.Collections.Generic.List<string>");
        }
    }
    public class DatabaseConnectionSelectorPropertyAttributeChecker : AttributeCheckPolicy
    {
        public DatabaseConnectionSelectorPropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.DatabaseConnectionSelectorPropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string");
        }
    }
    public class PageNamePropertyAttributeChecker : AttributeCheckPolicy
    {
        public PageNamePropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.PageNamePropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string");
        }
    }

    public class ServerCommandNamePropertyAttributeChecker : AttributeCheckPolicy
    {
        public ServerCommandNamePropertyAttributeChecker()
            : base("GrapeCity.Forguncy.Plugin.ServerCommandNamePropertyAttribute")
        {

        }

        public override string Check(AttributeData attribute, IPropertySymbol property)
        {
            return RequireType(attribute, property, "string");
        }
    }

    public class PropertyChecker
    {
        public static string CheckProperty(IPropertySymbol property)
        {
            var ignore = property.GetAttributes().FirstOrDefault(i =>
            i.AttributeClass.ToString() == "Newtonsoft.Json.JsonIgnoreAttribute" ||
            i.AttributeClass.ToString() == "System.ComponentModel.BrowsableAttribute");

            if (ignore != null)
            {
                return null;
            }

            if (property.DeclaringSyntaxReferences.Length == 1)
            {
                var syntax = property.DeclaringSyntaxReferences[0].GetSyntax();
                if (syntax is PropertyDeclarationSyntax declarationSyntax)
                {
                    var value = declarationSyntax.Initializer?.Value?.ToString();

                    if (!string.IsNullOrEmpty(value))
                    {
                        if (!property.Type.IsValueType)
                        {
                            return null;
                        }
                        var attrs = property.GetAttributes();
                        var defaultValue = attrs.FirstOrDefault(i => i.AttributeClass.ToString() == "System.ComponentModel.DefaultValueAttribute");

                        if (property.NullableAnnotation == NullableAnnotation.Annotated)
                        {
                            if (property.Type.SpecialType == SpecialType.System_Boolean ||
                                property.Type.SpecialType == SpecialType.System_Int16 ||
                                property.Type.SpecialType == SpecialType.System_Int32 ||
                                property.Type.SpecialType == SpecialType.System_Int64 ||
                                property.Type.SpecialType == SpecialType.System_Double)
                            {
                                if (value != "null")
                                {
                                    if (defaultValue == null || !Equals(defaultValue.ConstructorArguments.FirstOrDefault().Value, true))
                                    {
                                        return AttributeCheckPolicy.Prefix(property) + "可空值类型属性，默认值为" + value + " 时应该标注 DefaultValueAttribute 为 " + value + "，否则会导致属性设置为 null 时无法保存";
                                    }
                                }
                            }
                        }
                        if (property.Type.SpecialType == SpecialType.System_Boolean)
                        {
                            if (bool.TryParse(value, out var result))
                            {
                                if (result != false)
                                {
                                    if (defaultValue == null)
                                    {
                                        return AttributeCheckPolicy.Prefix(property) + "Bool属性，默认值为 true 时应该标注 DefaultValueAttribute 为 True，否则会导致属性设置为False时无法保存";
                                    }
                                    else
                                    {
                                        if (bool.TryParse(defaultValue.ConstructorArguments.FirstOrDefault().Value?.ToString(), out var result2))
                                        {
                                            if (result != result2)
                                            {
                                                return AttributeCheckPolicy.Prefix(property) + "Bool属性，默认值为 true 时应该标注 DefaultValueAttribute 为 True，否则会导致属性设置为False时无法保存";
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        if (property.Type.SpecialType == SpecialType.System_Int16 ||
                            property.Type.SpecialType == SpecialType.System_Int32 ||
                            property.Type.SpecialType == SpecialType.System_Int64)
                        {
                            if (long.TryParse(value, out var result))
                            {
                                if (result != 0)
                                {
                                    if (defaultValue == null)
                                    {
                                        return AttributeCheckPolicy.Prefix(property) + property.Type + "属性，默认值为 " + result + " 时应该标注 DefaultValueAttribute 为 " + result + "，否则会导致属性设置为0时无法保存";
                                    }
                                    else
                                    {
                                        if (long.TryParse(defaultValue.ConstructorArguments.FirstOrDefault().Value?.ToString(), out var result2))
                                        {
                                            if (result != result2)
                                            {
                                                return AttributeCheckPolicy.Prefix(property) + property.Type + "属性，默认值为 " + result + " 时应该标注 DefaultValueAttribute 为 " + result + "，否则会导致属性设置为0时无法保存";
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        if (property.Type.SpecialType == SpecialType.System_Double)
                        {
                            if (double.TryParse(value, out var result))
                            {
                                if (result != 0)
                                {
                                    if (defaultValue == null)
                                    {
                                        return AttributeCheckPolicy.Prefix(property) + property.Type + "属性，默认值为 " + result + " 时应该标注 DefaultValueAttribute 为 " + result + "，否则会导致属性设置为0时无法保存";
                                    }
                                    else
                                    {
                                        if (double.TryParse(defaultValue.ConstructorArguments.FirstOrDefault().Value?.ToString(), out var result2))
                                        {
                                            if (result != result2)
                                            {
                                                return AttributeCheckPolicy.Prefix(property) + property.Type + "属性，默认值为 " + result + " 时应该标注 DefaultValueAttribute 为 " + result + "，否则会导致属性设置为0时无法保存";
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return null;
        }
    }
}
